/**
 * 有效数字
 *
 * 给定一个字符串 s ，返回 s 是否是一个 有效数字。
 * 例如，下面的都是有效数字："2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1",
 * "53.5e93", "-123.456e789"，而接下来的不是："abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"。
 * 一般的，一个 有效数字 可以用以下的规则之一定义：
 * 一个 整数 后面跟着一个 可选指数。
 * 一个 十进制数 后面跟着一个 可选指数。
 * 一个 整数 定义为一个 可选符号 '-' 或 '+' 后面跟着 数字。
 * 一个 十进制数 定义为一个 可选符号 '-' 或 '+' 后面跟着下述规则：
 * 数字 后跟着一个 小数点 .。
 * 数字 后跟着一个 小数点 . 再跟着 数位。
 * 一个 小数点 . 后跟着 数位。
 * 指数 定义为指数符号 'e' 或 'E'，后面跟着一个 整数。
 * 数字 定义为一个或多个数位。
 *
 * 示例 1：
 * 输入：s = "0"
 * 输出：true
 *
 * 示例 2：
 * 输入：s = "e"
 * 输出：false
 *
 * 示例 3：
 * 输入：s = "."
 * 输出：false
 *
 * 提示：
 * 1 <= s.length <= 20
 * s 仅含英文字母（大写和小写），数字（0-9），加号 '+' ，减号 '-' ，或者点 '.' 。
 */

/**
 * 首先我们想写好这一题, 我们要知道什么才是一个数
 * 有两种情况:
 *  1.整数 + 指数
 *  2.十进制数 + 指数
 * 知道了两种情况我们来看看什么算是指数, 指数其实就是 e + 整数(可以有符号位)
 * 整数是啥, 就是可以带符号位 (+, -), 然后后面接上整数, 就是整数了
 * 什么是十进制数, 就是符号位 (+, -), 后面还可以有可以是 数字 + 小数点 + 数字
 * 也可以是单独的小数点 + 数字
 * 还可以是 数字 + 小数点
 * 这就是判断是否是数字
 * 我们知道了什么是数, 其实我们看, e 前面的需要 判断, 后面的也要判断
 * 那么我们就拿 e 作为指标, 分别判断两边是否 满足要求
 * 要是没有 e 的 话, 我们就直接判断就可以了
 * 时间复杂度 : O(n)
 * 空间复杂度 : O(n)
 */

public class Main {

    // 测试用例
    public static void main(String[] args) {
        Main test = new Main();
        System.out.println(test.isNumber("te1"));
    }

    public boolean isNumber(String ss) {

        // 因为指标 可能是 e 或者 是 E , 后面还要判断是否是 字母
        // 咱干脆将所有的字母全都转化为 大写字母
        ss = ss.toUpperCase();

        // 我们先查看下 第一个 和 最后一个是否为 e, 这两个特殊的情况会归结到 n == 1
        // 中, 但是他们是错误的
        if (ss.charAt(0) == 'E' || ss.charAt(ss.length() - 1) == 'E') {
            return false;
        }

        // 用 e 进行分割
        String[] s = ss.split("E");

        // 看分割除了几个
        int n = s.length;

        if (n == 1) {

            // 没有 e, 那么我们判断是否为 整数 或者 为 十进制数就可以了
            // 这里只要有一个符合要求就是 有效的数字
            return isNumb(s[0]) || isDoub(s[0]);
        } else if (n == 2) {

            // 只有一个 e, 那么我们需要判断前面的是否为正数 或者 十进制数, 还要判断后面的是否为 整数
            // 这里的前面和后面的同=同时成立才 是有效的
            return (isNumb(s[0]) || isDoub(s[0])) && isNumb(s[1]);
        } else {

            // 要是含有多个 e, 那么是不行的
            return false;
        }
    }

    /**
     * 判断 s 是否为 整数
     *
     * @param s
     * @return
     */
    private boolean isNumb (String s) {

        // 传入 字符串的长度
        int n = s.length();

        // 定义开始遍历的下标
        int i = 0;

        // 要是有符号位, 我们过滤出去
        if (s.charAt(i) == '-' || s.charAt(i) == '+') {
            i++;
        }

        // 当然要是 符号位 后面就空了, 明显也是不符合要求的
        if (i == n) {
            return false;
        }

        // 这里我们开始遍历
        for (; i < n; i++) {

            // 要是 遇到 . 或者 遇到 字符 (大写, 包括 +, -), 都是不符合要求的
            if (s.charAt(i) == '.' || isChar(s.charAt(i))) {
                return false;
            }
        }

        // 没有问题就返回 true
        return true;
    }

    /**
     * 判断 s 是否为小数
     *
     * @param s
     * @return
     */
    private boolean isDoub (String s) {

        // 长度
        int n = s.length();

        // 注意 我们这里要 设置一个 变量来记录 .
        // 为什么呢, 因为我们后面只是排除了一个 . , 后面要是还有 . 的话
        // 那么就是无效的, 但是要是开头没有 . , . 可能在后面出现, 所以
        // 我们要用一个变量来记录 . 是否已经出现过
        boolean tmp = false;

        // 开始遍历的下标
        int i = 0;

        // 先排除符号位
        if (s.charAt(i) == '-' || s.charAt(i) == '+') {
            i++;
        }

        // !!!!!!!!!!!!!!!!!!!!
        // 这里 要加上 i < n
        // 排除第一个是 .
        if (i < n && s.charAt(i) == '.') {
            i++;

            // 要是出现 . , 标记为 true
            tmp = true;
        }

        // 要是后面就没有 东西了, 是无效的
        if (i == n) {
            return false;
        }

        for (; i < n; i++) {

            // 先判断是否为 特殊字符
            if (isChar(s.charAt(i))) {
                return false;
            }

            // 判断是否有 .
            if (s.charAt(i) == '.') {

                // 要是第二次出现 . , 那么是无效的
                if (tmp) {
                    return false;
                } else {

                    // 第一次出现的话, 不用管, 可以给他一次机会
                    // 标记为 true
                    tmp = true;
                }
            }
        }

        // 中间没有出现问题的话 这部分就是有效的
        return true;
    }

    /**
     * 判断字符 ch 是否为 A~Z, 或者是 +, -
     *
     * @param ch
     * @return
     */
    private boolean isChar (char ch) {
        if ('A' <= ch && ch <= 'Z' || ch == '+' || ch == '-') {
            return true;
        } else {
            return false;
        }
    }
}